#include "i2c_bsp.h"
#include "cmsis_os.h"

i2c_obj_t i2c_obj_tbl[I2C_OBJ_NUM_MAX]=
{
#if defined(I2C1_OBJ_EN)
	{
		 .clk_pin.port = GPIOB,
		 .clk_pin.pin  = GPIO_PIN_6,
		 .sda_pin.port = GPIOB,
		 .sda_pin.pin  = GPIO_PIN_7,
	},
#endif
#if defined(I2C2_OBJ_EN)
	{
		 .clk_pin.port = GPIOB,
		 .clk_pin.pin  = GPIO_PIN_10,
		 .sda_pin.port = GPIOB,
		 .sda_pin.pin  = GPIO_PIN_11,
	},
#endif
#if defined(I2C3_OBJ_EN)
	{
		 .clk_pin.port = GPIOC,
		 .clk_pin.pin  = GPIO_PIN_9,
		 .sda_pin.port = GPIOA,
		 .sda_pin.pin  = GPIO_PIN_8,
	},
#endif
};

void i2c_obj_close(i2c_obj_id_t obj_id)
{
  if (obj_id >= I2C_OBJ_NUM_MAX)
    return;
  i2c_obj_t *obj = &i2c_obj_tbl[obj_id];

  //taskENTER_CRITICAL();
  HAL_I2C_MspDeInit(obj->hi2c);
  vSemaphoreDelete(obj->mutex);
  obj->hi2c = NULL;
  obj->mutex = NULL;
  //taskEXIT_CRITICAL();
}

int i2c_obj_open(i2c_obj_id_t obj_id, uint32_t clk_speed)
{
  i2c_obj_t *obj = NULL;

  if (obj_id >= I2C_OBJ_NUM_MAX)
  {
    return -1;
  }
  obj = &i2c_obj_tbl[obj_id];
  if(gpio_obj_open(&obj->clk_pin, 0, 1) == 0
	&&gpio_obj_open(&obj->sda_pin, 0, 1) == 0)
  {
	  for(int i = 0; i < 9; i++)
	  {
		  gpio_obj_write(&obj->clk_pin, 0);
		  HAL_Delay(1);
		  gpio_obj_write(&obj->clk_pin, 1);
		  HAL_Delay(1);
	  }
  }
#ifdef I2C1_OBJ_EN
  if (obj_id == I2C1_OBJ)
  {
    obj->hi2c = &hi2c1;
    obj->hi2c->Instance = I2C1;
  }
#endif
#ifdef I2C2_OBJ_EN
  if (obj_id == I2C2_OBJ)
  {
    obj->hi2c = &hi2c2;
    obj->hi2c->Instance = I2C2;
  }
#endif
#ifdef I2C3_OBJ_EN
  if (obj_id == I2C3_OBJ)
  {
    obj->hi2c = &hi2c3;
    obj->hi2c->Instance = I2C3;
  }
#endif
  if (obj->hi2c == NULL)
    return -1;
  if (obj->mutex == NULL)
    obj->mutex = xSemaphoreCreateMutex();
  obj->clk_speed = clk_speed;
  obj->hi2c->Init.ClockSpeed = clk_speed;
  obj->hi2c->Init.DutyCycle = I2C_DUTYCYCLE_2;
  obj->hi2c->Init.OwnAddress1 = 0;
  obj->hi2c->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  obj->hi2c->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  obj->hi2c->Init.OwnAddress2 = 0;
  obj->hi2c->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  obj->hi2c->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(obj->hi2c) != HAL_OK)
  {
    Error_Handler();
  }

  return 0;
}

int i2c_obj_read(i2c_obj_id_t obj_id, uint8_t chip_addr, uint16_t reg, reg_bits_t reg_bits, uint8_t *dbuf, uint8_t dsize)
{
  i2c_obj_t *obj = NULL;

  if (dbuf == NULL)
    return -1;

  if (obj_id >= I2C_OBJ_NUM_MAX)
  {
    return -1;
  }

  obj = &i2c_obj_tbl[obj_id];
  if (obj->hi2c == NULL && obj->mutex == NULL)
  {
    if (i2c_obj_open(obj_id, 100000) == -1)
    	return -1;
  }

  uint8_t txbuf[2];
  uint8_t offset = 0;

  txbuf[0] = reg >> 8;
  txbuf[1] = (uint8_t)reg;
  if (reg_bits == reg_8bits)
  {
    offset = 1;
  }

  xSemaphoreTake(obj->mutex, portMAX_DELAY);
  int retry = 3;
  int result = 0;
  while(retry--)
  {
    result = HAL_I2C_Master_Transmit(obj->hi2c, chip_addr, txbuf + offset, reg_bits, 0xffffffff);
    if (HAL_BUSY == result)
    {
      HAL_I2C_DeInit(obj->hi2c);
      i2c_obj_open(obj_id, obj->clk_speed);
      printf("%s %d:I2C bus busy, reopen i2c bus\r\n", __FUNCTION__, __LINE__);
    }
    else
      break;
  }
  if (HAL_OK != result)
  {
    xSemaphoreGive(obj->mutex);
    return -1;
  }
  if (HAL_OK != HAL_I2C_Master_Receive(obj->hi2c, chip_addr, (uint8_t *)dbuf, dsize, 500))
  {
    xSemaphoreGive(obj->mutex);
    return -1;
  }
  xSemaphoreGive(obj->mutex);
  return 0;
}

int i2c_obj_write(i2c_obj_id_t obj_id, uint8_t chip_addr, uint16_t reg, reg_bits_t reg_bits, uint8_t *dbuf, uint8_t dsize)
{
  uint8_t *txbuf;
  uint8_t offset = 0;
  int ret = -1;

  i2c_obj_t *obj = NULL;

  if (obj_id >= I2C_OBJ_NUM_MAX)
  {
    return -1;
  }
  obj = &i2c_obj_tbl[obj_id];
  if (obj->hi2c == NULL && obj->mutex == NULL)
  {
    if (i2c_obj_open(obj_id, 100000) == -1)
    	return -1;
  }

  txbuf = pvPortMalloc(dsize + 2);
  if (txbuf == NULL)
    return -1;

  txbuf[0] = reg >> 8;
  txbuf[1] = (uint8_t)reg;
  if (reg_bits == reg_8bits)
  {
    offset = 1;
  }

  for (uint8_t i = 0; i < dsize; i++)
    txbuf[2 + i] = dbuf[i];

  xSemaphoreTake(obj->mutex, portMAX_DELAY);
  int retry = 3;
  int result = 0;
  while (retry--)
  {
    result = HAL_I2C_Master_Transmit(obj->hi2c, chip_addr, txbuf + offset, reg_bits + dsize, 500);
    if (HAL_BUSY == result)
    {
      HAL_I2C_DeInit(obj->hi2c);
      i2c_obj_open(obj_id, obj->clk_speed);
      printf("%s %d:I2C bus busy, reopen i2c bus\r\n", __FUNCTION__, __LINE__);
    }
    else
    	break;
  }
  if (HAL_OK == result)
  {
    ret = 0;
  }
  xSemaphoreGive(obj->mutex);

  vPortFree(txbuf);
  return ret;
}
